package jdesktop.appbar.win32;

import com.sun.jna.Pointer;

import java.lang.reflect.Proxy;

/**
 * An AppBar window procedure that
 * provides different behaviours according to whether the the window is docked or
 * floating. The different behaviours are provided by delegating
 * to {@link DockedWndProc} or {@link FloatingWndProc} respectively.
 */
public class AppBarWndProc implements User32.WndProc
{
    private final DockedWndProc docked;
    private final FloatingWndProc floating;

    /**
     * The original window procedure.
     */
    private final Pointer delegate;

    private final WAppBar owner;

    public AppBarWndProc(WAppBar owner, AppBarEventHandler notify, Pointer delegate)
    {
        this.owner = owner;
        this.delegate = delegate;
        WndProcState state = new WndProcState(
                delegate,
                owner.getDragGuide(),
                owner,
                owner,
                createMessageHandler(owner));
        docked = new DockedWndProc(state, swingThread(notify, AppBarEventHandler.class));
        floating = new FloatingWndProc(state);
    }

    private <T> T swingThread(T target, Class<T> cls)
    {
        return (T)Proxy.newProxyInstance(
                     target.getClass().getClassLoader(),
                     new Class[] { cls },
                     new SwingInvokeLaterHandler(target));
    }

    protected WindowMessageHandler createMessageHandler(WindowMessageHandler target)
    {
        return new WindowsThreadMessageHandler(swingThread(target, WindowMessageHandler.class));
    }

    public int callback(Pointer hWnd, int uMsg, int uParam, int lParam)
    {
        int result;
        if (owner.isDocked())
            result = docked.callback(hWnd, uMsg, uParam, lParam);
        else
            result = floating.callback(hWnd, uMsg, uParam, lParam);
        return result;
    }

    static public AppBarWndProc install(WAppBar owner, AppBarEventHandler events)
    {
        User32 user32 = User32.INSTANCE;
        Pointer hWnd = owner.getWindowHandle();
        Pointer wndProc = new Pointer(user32.GetWindowLongW(hWnd, User32.GWL_WNDPROC));
        AppBarWndProc newWndProc = new AppBarWndProc(owner, events, wndProc);
        user32.SetWindowLongW(hWnd, User32.GWL_WNDPROC, newWndProc);
        return newWndProc;
    }

    public void uninstall()
    {
        uninstall(delegate, owner.getWindowHandle());
    }

    static private void uninstall(Pointer oldProc, Pointer hWnd)
    {
        User32.INSTANCE.SetWindowLongW(hWnd, User32.GWL_WNDPROC, oldProc);
    }

}



